﻿// Inner Fire 游戏引擎库
// Attacher - 附加组件
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：应用层(4)
// 将项目附加到一个骨骼上面。用于武器等项目。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-06-12

#include "attacher.h"
#include <game/animation/animator.h>
#include <game/animation/armature.h>
#include <game/animation/avatar.h>
#include <game/object/game_object.h>
#include <klog.h>

namespace ifire::game {

Attacher::Attacher(GameObject* game_object)
    : Component(ComponentType(), game_object)
    , use_modify_(false)
    , modify_mat_(ID_MAT) {
  // 保存原始的矩阵以进行之后的运算
  XMStoreFloat4x4(&origin_mat_, game_object_->GetTransform().mat);
}

Attacher::~Attacher() {}

void Attacher::AttachTo(GameObject* obj, ikit::sstr slot) {
  attach_to_ = obj;
  animator_ = (Animator*)attach_to_->GetComponent(Animator::ComponentType());
  auto armature_ = animator_->GetArmature();
  avatar_ = armature_->GetAvatar();
  slot_name_ = slot;
  slot_ = avatar_->GetAttachSlot(slot_name_);
  if (!slot_) {
    KERROR("Avatar(%s) has no slot: %s", avatar_->name.c_str(),
        slot_name_.c_str());
  } else {
    auto node = armature_->GetNodeTree()->Find(slot_->bone);
    if (!node) {
      KERROR("Armature has no bone: %s", slot_->bone.c_str());
      bone_id_ = -1;
    } else {
      bone_id_ = node->bone_id;
    }
  }
}

void Attacher::FixedUpdate(float delta_time) {}

XMMATRIX AttachWithoutDeformation(
    const XMMATRIX& bone_mat, const XMMATRIX& own_mat) {
  // 分解骨骼矩阵
  XMVECTOR bone_scale, bone_rotation, bone_translation;
  XMMatrixDecompose(&bone_scale, &bone_rotation, &bone_translation, bone_mat);

  // 分解物体矩阵
  XMVECTOR own_scale, own_rotation, own_translation;
  XMMatrixDecompose(&own_scale, &own_rotation, &own_translation, own_mat);

  // 重建变换矩阵 - 使用骨骼的位置和旋转，但保持物体的缩放
  XMMATRIX result =
      XMMatrixScalingFromVector(own_scale) *           // 使用物体原始缩放
      XMMatrixRotationQuaternion(own_rotation) *       // 物体自身旋转
      XMMatrixTranslationFromVector(own_translation) * // 物体相对于骨骼的偏移
      XMMatrixRotationQuaternion(bone_rotation) *      // 骨骼旋转
      XMMatrixTranslationFromVector(bone_translation); // 骨骼位置

  return result;
}

void Attacher::LateUpdate(float delta_time) {
  if (!attach_to_) {
    return;
  }

  // 获得父级的变换
  auto parent_mat = attach_to_->GetTransform().mat;
  // 得到变换的修正
  auto fixed_mat = ID_MAT;

  if (use_modify_) {
    fixed_mat = modify_mat_;
  } else {
    PickAdjust(fixed_mat);
  }

  // 得到骨骼的变换
  auto bone_mat = ID_MAT;
  if (bone_id_ != -1) {
    bone_mat = animator_->GetBoneFinalMatrix(bone_id_);
  }

  // 得到自已的变换
  auto own_mat = XMLoadFloat4x4(&origin_mat_);

  // 先计算出Attach矩阵
  auto attach_mat = AttachWithoutDeformation(bone_mat, own_mat * fixed_mat);

  // 直接设置最终矩阵
  game_object_->GetTransform().SetWorldMatrix(attach_mat * parent_mat);
  game_object_->UpdateTransform();
  //game_object_->SetRenderMatrix(attach_mat * parent_mat);
}

void Attacher::SetModify(IVec3 position, IVec3 rotation, IVec3 scale) {
  Trans3 trans(position, rotation, scale);
  modify_mat_ = trans.GetMatrix();
}

void Attacher::GetAdjust(IVec3& position, IVec3& rotation, IVec3& scale) const {
  auto& trans = slot_->GetDefault();
  position = trans.GetPosition();
  // 因为我的四元数转换并不能得到原来的数值，所以这里需要一个原生的项目（感觉以后修复起来太蛋痛了）
  rotation = slot_->rotation;
  scale = trans.GetScale();
}

void Attacher::PickAdjust(IMat& fixed_mat) {
  auto main_clip = animator_->GetMainClip();
  if (main_clip && slot_) {
    // 通过Slot和动画获得绑定的矩阵
    auto& trans = slot_->Pick(main_clip->GetAvatar()->name, main_clip->name);
    fixed_mat = trans.GetMatrix();
  }
}

ikit::sstr Attacher::ComponentType() {
  static ikit::sstr TYPE_NAME("Attacher");
  return TYPE_NAME;
}

const char* Attacher::GetMetaName() { return "AttacherMeta"; }

} // namespace ifire::game